package com.webcamtracker.move.gestures;

import com.webcamtracker.move.tracking.TrackingEvent;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Responsible for processing move events and recognition of mouse movements.
 *
 * @author Smardec
 * @version 1.2
 */
class MoveGesturesRecognizer {
    /**
     * Grid size. Default is 30.
     */
    private int gridSize = 80;
    /**
     * Reference to {@link MoveGestures}.
     */
    private MoveGestures moveGestures;
    /**
     * Start point for current movement.
     */
    private Point startPoint = null;
    /**
     * List representation of gesture.
     */
    private List<MoveType> gesture = new ArrayList<MoveType>();

    /**
     * Creates MouseGesturesRecognizer.
     *
     * @param mouseGestures Reference to {@link MoveGestures}
     */
    MoveGesturesRecognizer(MoveGestures mouseGestures) {
        this.moveGestures = mouseGestures;
    }

    /**
     * Processes move event.
     *
     * @param trackingEvent MouseEvent
     */
    void processMoveEvent(TrackingEvent trackingEvent) {
        /*if (!(trackingEvent.getSource() instanceof Component))
            return;*/

        Point moveEventPoint = trackingEvent.getPoint();

        /*SwingUtilities.convertPointToScreen(moveEventPoint, (Component) trackingEvent.getSource());*/

        if (startPoint == null) {
            startPoint = moveEventPoint;
            return;
        }

        int deltaX = getDeltaX(startPoint, moveEventPoint);
        int deltaY = getDeltaY(startPoint, moveEventPoint);
        int absDeltaX = Math.abs(deltaX);
        int absDeltaY = Math.abs(deltaY);

        if ((absDeltaX < gridSize) && (absDeltaY < gridSize))
            return;
        float absTangent = ((float) absDeltaX) / absDeltaY;
        if (absTangent < 1) {
            if (deltaY < 0)
                saveMove(MoveType.UP);
            else
                saveMove(MoveType.DOWN);
        } else {
            if (deltaX < 0)
                saveMove(MoveType.LEFT);
            else
                saveMove(MoveType.RIGHT);
        }
        startPoint = moveEventPoint;
    }

    /**
     * Returns delta x.
     *
     * @param a First point
     * @param b Second point
     * @return Delta x
     */
    private int getDeltaX(Point a, Point b) {
        return b.x - a.x;
    }

    /**
     * Returns delta y.
     *
     * @param a First point
     * @param b Second point
     * @return Delta y
     */
    private int getDeltaY(Point a, Point b) {
        return b.y - a.y;
    }

    /**
     * Adds movement to buffer.
     *
     * @param move String representation of recognized movement
     */
    private void saveMove(MoveType move) {

        // should not store two equal moves in succession
        if ((gesture.size() > 0) && (gesture.get(gesture.size() - 1) == move))
            return;

        gesture.add(move);
        moveGestures.fireGestureMovementRecognized(getGesture());
    }

    /**
     * Returns current grid size (minimum mouse movement length to be recognized).
     *
     * @return Grid size in pixels. Default is 30.
     */
    int getGridSize() {
        return gridSize;
    }

    /**
     * Sets grid size (minimum mouse movement length to be recognized).
     *
     * @param gridSize New grid size in pixels
     */
    void setGridSize(int gridSize) {
        this.gridSize = gridSize;
    }

    /**
     * Returns List representation of move gesture.
     *
     * @return Move representation of mouse gesture. List of MoveType : MoveType.LEFT for left,
     *         MoveType.RIGHT for right, MoveType.UP for up, MoveType.DOWN for down movements. For example: "ULD".
     */
    List<MoveType> getGesture() {
        return gesture;
    }

    /**
     * Indicates whether any movements were recognized.
     *
     * @return <code>true</code> if there are recognized movements; <code>false</code> otherwise
     */
    boolean isGestureRecognized() {
        return !gesture.isEmpty();
    }

    /**
     * Clears temporary info about previous gesture.
     */
    void clearTemporaryInfo() {
        startPoint = null;
        gesture.clear();
    }
}
